/*
这段demo的主要目的是写一份可以随时拷贝的参数解析小样。
为了验证功能，杜撰了一个“在文本文件和二进制文件之间做转换”的功能。
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

#define MAX_BUFF_LEN 500

void display_usage()
{
    printf("Usage: Use to translate between txt and bin, default is txt2bin,\n"
           "you can use txt file or enter on terminal directly as input.\n"
           "you can select filename or print on terminal directly as output.\n"
           "cmdparse [-t] //mode -- translate txt to bin\n"
           "         [-b] //mode -- translate bin to txt\n"
           "         [-p <path>] //use file as input\n"
           "         [-n <name>] //name ouput file\n"
           "         [-d <data>] //enter data on terminal\n"
           );
}

typedef struct Ctrler
{
    int mode; // 0(default) for txt2bin, 1 for bin2txt;
    char *srcfile;
    char *srcdata;
    char *dstfile;
    char *dstdata;
} Ctrler;

int ParseParam(int argc, char *argv[], Ctrler *ctrler)
{
    char ch;
    while ((ch = getopt(argc, argv, "tbhp:n:d:")) != -1)
    {
        switch (ch)
        {
        case 't':
            printf("You want to use to translate file from text to binary\n");
            ctrler->mode = 0;
            break;
        case 'b':
            printf("You want to use to translate file from binary to text\n");
            ctrler->mode = 1;
            break;
        case 'p':
            printf("Your entered src path is %s\n", optarg);
            ctrler->srcfile = optarg;
            break;
        case 'n':
            printf("Your entered dst name is %s\n", optarg);
            ctrler->dstfile = optarg;
            break;
        case 'd':
            printf("Your entered src data is %s\n", optarg);
            ctrler->srcdata = optarg;
            break;
        case '?':
            printf("Unknown option: %c\n", (char)optopt);
        default:
            display_usage();
            return -1;
        }
    }
    return 0;
}

int CheckParam(Ctrler *ctrler)
{
    if (ctrler->mode != 0 && ctrler->mode != 1)
    {
        printf("Please select correct mode 0 or 1\n");
        return -1;
    }
    if (ctrler->srcdata == NULL)
    {
        if (ctrler->srcfile == NULL)
        {
            printf("please use -p to select a src path or -d to input src data\n");
            return -1;
        }
        else
        {
            int fp = open(ctrler->srcfile, O_RDONLY);
            if (fp == -1)
            {
                printf("src path is not exist\n");
                return -1;
            }
            close(fp);
        }
    }
    else
    {
        if(ctrler->mode == 1)
        {
            printf("you can't use terminal to enter bin\n");
            return -1;
        }
    }
    return 0;
}

int char2int['f'];
char int2char[16];
void Mapcharandint()
{
    char ch;
    int in;
    for (ch = '0', in = 0; in < 10; ch++, in++)
    {
        char2int[ch] = in;
    }
    for (ch = 'a', in = 10; in < 16; ch++, in++)
    {
        char2int[ch] = in;
    }
    for (ch = 'A', in = 10; in < 16; ch++, in++)
    {
        char2int[ch] = in;
    }
    for (ch = '0', in = 0; in < 10; ch++, in++)
    {
        int2char[in] = ch;
    }
    for (ch = 'a', in = 10; in < 16; ch++, in++)
    {
        int2char[in] = ch;
    }
}

int txt2bin(char *src, int srclen, char *dst)
{
    int count = 0;
    while(*src != '\0' && count < srclen)
    {
        if(*src > 'f')  return -1;
        if((count & 0x1) == 0)
            *dst = (char2int[*src] << 4);
        else {
            *dst += char2int[*src];
            dst++;
        }
        src++;
        count++;
    }
    return 0;
}

int bin2txt(char *src, int srclen, char *dst)
{
    int count = 0;
    while(count < srclen)
    {
        int high = ((*src >> 4) & 0x0F);
        int low  = *src & 0x0F;
        if(high > 15 || low > 15)  return -1;

        *dst = (int2char[high]);
        dst++;
        *dst = (int2char[low]);
        dst++;

        src++;
        count++;
    }
    *dst = '\0';
    return 0;
}

int Translate(Ctrler *ctrler)
{
    char buff[MAX_BUFF_LEN];
    char dst[MAX_BUFF_LEN];
    int bytes;
    memset(buff, 0, MAX_BUFF_LEN);
    memset(dst, 0, MAX_BUFF_LEN);
    int fp;

    if (ctrler->srcdata == NULL)
    {
        fp = open(ctrler->srcfile, O_RDONLY);
        if (fp == -1)
        {
            printf("open error\n");
            return -1;
        }
        bytes = read(fp, buff, MAX_BUFF_LEN);
        if (bytes < 0)
        {
            printf("read error\n");
            close(fp);
            return -1;
        }
    }
    else
    {
        bytes = strlen(ctrler->srcdata);
        memcpy(buff, ctrler->srcdata, strlen(ctrler->srcdata));
    }

    int ret = -1;
    if (ctrler->mode == 0)
    {
        ret = txt2bin(buff, bytes, dst);
        if (ctrler->dstfile == NULL)
            ctrler->dstfile = "binfile";
    }
    else
    {
        ret = bin2txt(buff, bytes, dst);
        if (ctrler->dstfile == NULL)
            ctrler->dstfile = "txtfile.txt";
    }

    int wfp = open(ctrler->dstfile, O_RDWR | O_CREAT, 0777);
    if (wfp == -1 || ret == -1)
    {
        printf("open dstfile or input error\n");
        close(fp);
        return -1;
    }
    int wbytes = write(wfp, dst, bytes);
    if (wbytes < 0)
    {
        printf("write error\n");
        close(fp);
        close(wfp);
        return -1;
    }
    close(fp);
    close(wfp);
    return 0;
}

int main(int argc, char *argv[])
{
    Ctrler ctrler;
    memset(&ctrler, 0, sizeof(ctrler));
    Mapcharandint();

    if (0 != ParseParam(argc, argv, &ctrler))
        return -1;

    if (0 != CheckParam(&ctrler))
    {
        printf("CheckParam ERROR\n");
        display_usage();
        return -1;
    }

    if (0 != Translate(&ctrler))
    {
        printf("Translate Error\n");
        return -1;
    }
    printf("Translate Success\n");
    return 0;
}